package studio.xiaoyun.core.parameter;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import studio.xiaoyun.core.exception.CoreException;
import studio.xiaoyun.core.exception.ErrorCode;
import studio.xiaoyun.core.parameter.criterion.Criterion;
import studio.xiaoyun.core.parameter.criterion.CriterionAnd;
import studio.xiaoyun.core.parameter.criterion.CriterionOr;
import studio.xiaoyun.core.parameter.criterion.Query;

/**
 * 所有参数类的超类
 * 
 */
public abstract class Parameter implements Serializable {

	
	private static final long serialVersionUID = 8599993944205393403L;
	/**
	 * 添加的搜索条件
	 */
	private List<Criterion> criterions;
	/**
	 * 默认的搜索模式，可选项：" and "," or ",默认是" and "
	 */
	private String defaultMode = " and ";
	/**
	 * 第一条记录的位置
	 */
	private int firstResult = 0;
	/**
	 * 返回值中包括的字段，不同字段之间使用','分隔
	 */
	private String includeField = "";
	/**
	 * 是否升序，由0和1组成的字符串,0 降序 ,1 升序
	 */
	private String isAsc = "";

	/**
	 * 最多返回的记录数
	 */
	private int maxResults = 20;
	
	/**
	 * 排序字段,不同字段之间用','分隔
	 */
	private String sortField = "";
	
	
	/**
	 * 添加返回值中包括的字段。
	 * <p>可以为null，默认返回所有数据，使用该方法可以设置只返回某些字段的数据，没有设置的数据将不会返回
	 * @param fields 返回值中包括的字段
	 * @return Parameter的实例
	 * @since 1.0.0
	 */
	public Parameter addIncludeField(String... fields){
		if(fields==null || fields.length==0){
			return this;
		}
		for(String field:fields){
			if (field != null && field.matches("[0-9a-zA-Z_]+")) {
				if (this.includeField.equals("")) {
					this.includeField = field;
				} else {
					this.includeField += "," + field;
				}
			} else {
				throw new CoreException(ErrorCode.PARAMETER_ERROR,"参数错误:"+field);
			}
		}
		return this;
	}

	/**
	 * 添加搜索条件。
	 * <p>示例：parameter.addQuery(Query.equals("name","test"));
	 * @param query 搜索条件
	 * @return Parameter的实例
	 * @since 1.0.0
	 */
	public Parameter addQuery(Criterion query) {
		if(criterions==null){
			this.criterions = new LinkedList<>();
		}
		this.criterions.add(query);
		return this;
	}

	/**
	 * 添加排序参数。
	 * <p>在本类中有定义的属性都可以排序，没有定义的属性则不能排序
	 * @param sortField 排序字段
	 * @param isAsc 是否升序，true 升序，false 降序
	 * @return Parameter的实例
	 * @since 1.0.0
	 */
	public Parameter addSort(String sortField, boolean isAsc){
		if (sortField != null && sortField.matches("[0-9a-zA-Z_]+")) {
			if (isAsc) {
				this.isAsc += "1";
			} else {
				this.isAsc += "0";
			}
			if (this.sortField.equals("")) {
				this.sortField += sortField;
			} else {
				this.sortField += "," + sortField;
			}
		} else {
			throw new CoreException(ErrorCode.PARAMETER_ERROR,"参数错误:"+sortField);
		}
		return this;
	}

	/**
	 * 默认的查询条件
	 */
	protected List<Criterion> getDefaultValue(){
		List<Criterion> result = new LinkedList<>();
		return result;
	}
	
	/**
	 * 
	 * @return 最多返回的记录数
	 * @since 1.0.0
	 */
	public int getFirstResult() {
		return firstResult;
	}

	/**
	 * 取得返回值中要包含的字段
	 * @return 返回值中要包含的字段
	 * @since 1.0.0
	 */
	public List<String> getIncludeField(){
		String str = includeField.trim();
		if(str.equals("")){
			return Collections.emptyList();
		}else{
			String[] fields = str.split(",");
			return Arrays.asList(fields);
		}
	}

	/**
	 * 获得是否升序的数组。
	 * <p>该数组和排序字段的数组一一对应
	 * @return 是否升序
	 * @since 1.0.0
	 */
	public List<Boolean> getIsAsc() {
		if(isAsc.equals("")){
			return Collections.emptyList();
		}else{
			byte[] bytes = isAsc.getBytes();
			List<Boolean> result = new LinkedList<Boolean>();
			for (int i = 0; i < bytes.length; i++) {
				if (bytes[i] == '0') {
					result.add(false);
				} else {
					result.add(true);
				}
			}
			return result;
		}
	}

	/**
	 * 
	 * @return 第一条记录的位置
	 * @since 1.0.0
	 */
	public int getMaxResults() {
		return maxResults;
	}

	/**
	 * 获得Criterion中的所有属性
	 */
	private List<String> getPropertiesByCriterions(List<Criterion> criterions){
		if(criterions==null || criterions.isEmpty()){
			return Collections.emptyList();
		}
		List<String> result = new LinkedList<String>();
		for(Criterion criterion:criterions){
			result.addAll(criterion.getPropertyName());
		}
		return result;
	}
	
	/**
	 * 获得当前类实例中所有属性名及属性值，不包括Parameter类的属性
	 * @return key 属性名，value 属性值(可以为null)
	 */
	private Map<String, Object> getPropertyNameAndValue() {
		Map<String, Object> result = new HashMap<String, Object>();
		// 找到类的所有属性
		List<Field> fieldList = new LinkedList<Field>();
		Field[] fields = this.getClass().getDeclaredFields();
		fieldList.addAll(Arrays.asList(fields));
		Class<?> superClass = this.getClass().getSuperclass();
		while (superClass != Object.class && superClass != Parameter.class) {
			fields = superClass.getDeclaredFields();
			fieldList.addAll(Arrays.asList(fields));
			superClass = superClass.getSuperclass();
		}
		// 找到类的所有公共的方法
		Method[] methods = this.getClass().getMethods();
		Method[] excludeMethods = Parameter.class.getMethods();
		for (Method method : methods) {
			String name = method.getName();
			boolean isExclude = false;
			for(Method m:excludeMethods){
				if(m.getName().equals(name)){
					isExclude = true;
					break;
				}
			}
			if (!isExclude && name.startsWith("get") && name.length() > 4 
					&& method.getParameterCount() == 0 && method.getReturnType() != Void.TYPE) {
				Object obj = null;
				try {
					obj = method.invoke(this);
				} catch (Exception e) {}
				String fieldName = null;
				for (Field field : fieldList) {
					if (name.equalsIgnoreCase("get"+field.getName())) {
						fieldName = field.getName();
						break;
					}
				}
				if(fieldName!=null){
					result.put(fieldName, obj);
				}
			}
		}
		return result;
	}
	
	/**
	 * 获得字符串格式的查询条件
	 * @return 字符串格式的查询条件
	 * @since 1.0.0
	 */
	public String getQuery() {
		Map<String,Object> property = this.getPropertyNameAndValue();
		Map<String,String> mapping = new HashMap<String,String>();
		Iterator<String> it = property.keySet().iterator();
		while(it.hasNext()){
			String key = it.next();
			mapping.put(key, key);
		}
		return this.getQuery(mapping);
	}
	
	/**
	 * 获得字符串格式的查询条件
	 * @param mapping 设置生成的字符串中类成员变量和数据库字段的映射关系，Map的key是类的成员变量名称，value是数据库字段名称
	 * @return 字符串格式的查询条件
	 * @since 1.0.0
	 */
	public String getQuery(Map<String, String> mapping) {
		if(mapping==null){
			throw new CoreException("参数不能为null");
		}
		if(!validate()){
			throw new CoreException("查询条件存在错误!");
		}
		List<Criterion> cList = new LinkedList<Criterion>();
		
		//将类中非null的属性添加到查询条件中 
		Map<String,Object> properties = this.getPropertyNameAndValue();
		Iterator<String> it = properties.keySet().iterator();
		while(it.hasNext()){
			String key = it.next();
			Object value = properties.get(key);
			if(value!=null){
				cList.add(Query.equals(key, value));
			}
		}
		
		//添加使用addQuery()方法设置的查询条件
		if(this.criterions!=null){
			cList.addAll(criterions);
		}
		//添加默认查询条件
		cList.addAll(this.getDefaultValue());
		
		if(cList==null || cList.isEmpty()){
			return "";
		}
		
		//将查询条件转换为字符串
		List<String> propertieList = this.getPropertiesByCriterions(cList);
		String where = this.getStringByCriterions(cList);
		Object[] params = new Object[propertieList.size()];
		for (int i = 0; i < propertieList.size(); i++) {
			String param = mapping.get(propertieList.get(i));
			if (param != null) {
				params[i] = param;
			} else {
				throw new CoreException("没有找到属性"+propertieList.get(i)+"映射关系");
			}
		}
		String result = String.format(where, params);
		
		return result;
	}
	/**
	 * 获得排序的字段 <br/>
	 * 该数组和是否升序的数组一一对应
	 * @return 排序的字段
	 * @since 1.0.0
	 */
	public List<String> getSortField() {
		if(sortField.equals("")){
			return Collections.emptyList();
		}else{
			List<String> result = new LinkedList<String>();
			String[] sorts = sortField.split(",");
			for(int i=0;i<sorts.length;i++){
				result.add(sorts[i]);
			}
			return result;
		}
	}
	
	/**
	 * 将Criterion列表转换为字符串
	 */
	private String getStringByCriterions(List<Criterion> criterions){
		if(criterions==null || criterions.isEmpty()){
			return "";
		}
		StringBuffer result = new StringBuffer();
		for(Criterion criterion:criterions){
			if(criterion instanceof CriterionOr || criterion instanceof CriterionAnd){
				result.append("(");
			}
			result.append(criterion.getQuery(this));
			if(criterion instanceof CriterionOr || criterion instanceof CriterionAnd){
				result.append(")");
			}
			result.append(defaultMode);
		}
		result.delete(result.length()-defaultMode.length(), result.length());
		if(criterions.size()==1 && (criterions.get(0) instanceof CriterionAnd || criterions.get(0) instanceof CriterionOr)){
			result.deleteCharAt(0);
			result.deleteCharAt(result.length()-1);
		}
		return result.toString();
	}

	/**
	 * 设置第一条记录的位置
	 * @param firstResult 第一条记录的位置，从0开始
	 * @since 1.0.0
	 */
	public void setFirstResult(int firstResult) {
		if (firstResult < 0) {
			throw new CoreException(ErrorCode.PARAMETER_ERROR,"参数不能小于0");
		} else {
			this.firstResult = firstResult;
		}
	}

	/**
	 * 设置最多返回的记录数
	 * @param maxResults 最多返回的记录数,取值范围为：大于0，并且小于1001
	 * @since 1.0.0
	 */
	public void setMaxResults(int maxResults) {
		if (maxResults < 0 || maxResults > 1000) {
			throw new CoreException(ErrorCode.PARAMETER_ERROR, "参数应大于0,并且小于1001!");
		} else {
			this.maxResults = maxResults;
		}
	}

	/**
	 * 将类转换为字符串
	 * @return 字符串
	 * @since 1.0.0
	 */
	@Override
	public String toString() {
		String result;
		try {
			result = this.getQuery();
		} catch (Exception e) {
			result = super.toString();
		}
		return result;
	}
	/**
	 * 数据是否合法
	 * @return true 合法，false 不合法
	 * @since 1.0.0
	 */
	public boolean validate() {
		boolean flag = true;
		//获得类实例中的所有属性和值
		Map<String,Object> name = this.getPropertyNameAndValue();
		
		//验证添加的查询条件中的属性名在类实例中是否有定义
		List<String> propertyList = this.getPropertiesByCriterions(criterions);
		for(String property:propertyList){
			if(!name.containsKey(property)){
				flag = false;
				break;
			}
		}
		
		//验证排序字段是否在类实例中是否有定义
		List<String> sorts = this.getSortField();
		for(String sort:sorts){
			if(!name.containsKey(sort)){
				flag = false;
				break;
			}
		}
		
		//验证返回值中包含的字段在类实例中是否有定义
		List<String> includes = this.getIncludeField();
		for(String include:includes){
			if(!name.containsKey(include)){
				flag = false;
				break;
			}
		}
		
		return flag;
	}
	
}
